﻿#nullable enable
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Linq;

namespace Admin.NET.Core
{
    public static class DataConvertUtil
    {
        /// <summary>
        /// 将对象转换为Excel数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="sysDictTypeRep"></param>
        /// <param name="sysDictDataRep"></param>
        /// <param name="headers"></param>
        /// <param name="data"></param>
        /// <param name="sheetName"></param>
        public static void ToExcelData<T>(List<T> source,
            IRepository<SysDictType, MasterDbContextLocator> sysDictTypeRep,
            IRepository<SysDictData, MasterDbContextLocator> sysDictDataRep,
            out List<string> headers, out List<List<object>> data, out string sheetName)
        {
            headers = new List<string>();
            data = new List<List<object>>();
            var type = typeof(T);
            sheetName = ((DescriptionAttribute?)type.GetCustomAttribute(typeof(DescriptionAttribute), true))?.Description ?? "无";
            List<string> enumClassNames = new();
            foreach (var field in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if ("Id".Equals(field.Name)) continue;
                if (field.Name.EndsWith("Id")) continue;
                headers.Add(((DescriptionAttribute?)field.GetCustomAttribute(typeof(DescriptionAttribute), true))?.Description ?? "无");
                var dataType = StringUtil.ParseTrueType2(field.PropertyType.FullName ?? "");
                if (TypeUtil.GetType(dataType)?.IsEnum ?? false)
                {
                    if (!enumClassNames.Contains(dataType))
                    {
                        enumClassNames.Add(dataType);
                    }
                }
            }

            var dictTypeList = sysDictTypeRep.DetachedEntities.Where(x => enumClassNames.Contains(x.EnumClassName)).ToList();
            List<long> dictTypeIds = dictTypeList.Select(x => x.Id).ToList();
            Dictionary<long, SysDictType> dictTypeDictionary = new();
            dictTypeList.ForEach(x => dictTypeDictionary.Add(x.Id, x));
            var dictDataList = sysDictDataRep.DetachedEntities.Where(x => dictTypeIds.Contains(x.TypeId)).ToList();
            dictDataList.ForEach(x =>
            {
                if (dictTypeDictionary.ContainsKey(x.TypeId))
                {
                    var dictType = dictTypeDictionary[x.TypeId];
                    if (dictType?.SysDictDatas == null)
                    {
                        if (dictType != null)
                        {
                            dictType.SysDictDatas = new List<SysDictData>();
                        }
                    }
                    dictType?.SysDictDatas?.Add(x);
                }
            });
            Dictionary<string, string> map = new();
            dictTypeList.ForEach(x =>
            {
                if (x?.SysDictDatas != null)
                {
                    foreach (SysDictData dictData in x.SysDictDatas)
                    {
                        if (dictData != null) map.Add(x.EnumClassName + "#" + dictData.Code, dictData.Value);
                    }
                }
            });

            foreach (var t in source)
            {
                List<object> objectList = new();
                foreach (var field in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    var fieldName = field.Name;
                    if ("Id".Equals(fieldName)) continue;
                    if (field.Name.EndsWith("Id")) continue;
                    var v = t?.GetType()?.GetProperty(fieldName)?.GetValue(t);
                    var dataType = StringUtil.ParseTrueType2(field.PropertyType.FullName ?? "");
                    if (TypeUtil.GetType(dataType)?.IsEnum ?? false)
                    {
                        v = Convert.ToInt32(v);
                        if (map.ContainsKey(dataType + "#" + v)) v = map[dataType + "#" + v];
                    }
                    else if ("System.DateTimeOffset".Equals(dataType))
                    {
                        v = ExcelDateUtil.ToInt(
                            new DateTimeOffset(Convert.ToDateTime(((DateTimeOffset?)v)?.ToString("yyyy-MM-dd") ?? "1900-01-01")));
                    }
                    else if ("System.DateTime".Equals(dataType))
                    {
                        v = ExcelDateUtil.ToInt(
                            Convert.ToDateTime(((DateTime?)v)?.ToString("yyyy-MM-dd") ?? "1900-01-01"));
                    }
                    objectList.Add(v ?? "");
                }
                data.Add(objectList);
            }
        }

        /// <summary>
        /// 将对象转换为Excel数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="sysDictTypeRep"></param>
        /// <param name="sysDictDataRep"></param>
        /// <param name="headers"></param>
        /// <param name="data"></param>
        /// <param name="sheetName"></param>
        /// <param name="className"></param>
        public static void ToExcelData<T>(List<T> source,
            IRepository<SysDictType, MasterDbContextLocator> sysDictTypeRep,
            IRepository<SysDictData, MasterDbContextLocator> sysDictDataRep,
            out List<string> headers, out List<List<object>> data, out string sheetName, out string className)
        {
            ToExcelData(source, sysDictTypeRep, sysDictDataRep, out headers, out data, out sheetName);
            var type = typeof(T);
            className = type.Name ?? "";
            if (className.EndsWith("Output"))
            {
                className = className[..^"Output".Length];
            }
        }


        /// <summary>
        /// 根据键数字生成内容字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static string ParseStrByKeys<T>(this T t, string[] keys)
        {
            var type = typeof(T);
            Dictionary<string, PropertyInfo> namePropertyInfoDict = new();
            foreach (var field in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var fName = field?.Name ?? "";
                if (keys.Contains(fName))
                {
                    namePropertyInfoDict[fName] = field ?? null!;
                }
            }
            var str = "";

            foreach (var k in keys)
            {
                str += StringUtil.SplitChar() + namePropertyInfoDict[k]?.GetValue(t) ?? "";
            }

            return str;
        }

        /// <summary>
        /// 将Excel数据转换为对象列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="headers"></param>
        /// <param name="data"></param>
        /// <param name="sheetName"></param>
        /// <returns></returns>
        public static List<T> ToObjectList<T>(List<string> headers, List<List<object?>> data, string sheetName, string[] keys, int dataStartLine, out Dictionary<string, T> dict)
        {
            var type = typeof(T);
            //去掉此验证 【Editby shaocx,2024-04-15】
            //var sheetName2 = ((DescriptionAttribute?)type.GetCustomAttribute(typeof(DescriptionAttribute), true))?.Description ?? "无";
            //if (!sheetName2.Equals(sheetName))
            //{
            //    throw Oops.Oh(ErrorCode.Excel003);
            //}

            Dictionary<int, string> indexFieldNameDict = new();
            Dictionary<int, string> indexFieldTypeDict = new();
            Dictionary<int, PropertyInfo> indexPropertyInfoDict = new();
            Dictionary<string, PropertyInfo> namePropertyInfoDict = new();
            foreach (var field in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var desc = ((DescriptionAttribute?)field.GetCustomAttribute(typeof(DescriptionAttribute), true))?.Description ?? "无";
                var fName = field.Name ?? "";
                var fType = StringUtil.ParseTrueType2(field.PropertyType.FullName ?? "");
                for (var i = 0; i < headers.Count; i++)
                {
                    var header = headers[i];
                    if (header.Equals(desc))
                    {
                        indexFieldNameDict[i] = fName;
                        indexFieldTypeDict[i] = fType;
                        indexPropertyInfoDict[i] = field;
                        namePropertyInfoDict[fName] = field;
                        break;
                    }
                }
            }

            List<T> outputs = new();
            foreach (var record in data)
            {
                var output = Activator.CreateInstance(type, true);
                for (var i = 0; i < record.Count; i++)
                {
                    if ("System.String".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, record[i]?.ToString() ?? "", null);
                    }
                    else if ("System.Int32".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, int.Parse(record[i]?.ToString() ?? "0"), null);
                    }
                    else if ("System.Int64".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, long.Parse(record[i]?.ToString() ?? "0"), null);
                    }
                    else if ("System.Single".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, float.Parse(record[i]?.ToString() ?? "0.0"), null);
                    }
                    else if ("System.Double".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, double.Parse(record[i]?.ToString() ?? "0.0"), null);
                    }
                    else if ("System.Decimal".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, decimal.Parse(record[i]?.ToString() ?? "0.0"), null);
                    }
                    else if ("System.Boolean".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, bool.Parse(record[i]?.ToString() ?? "False"), null);
                    }
                    else if ("System.Byte".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output, byte.Parse(record[i]?.ToString() ?? "0"), null);
                    }
                    else if (TypeUtil.GetType(indexFieldTypeDict[i])?.IsEnum ?? false)
                    {
                        indexPropertyInfoDict[i].SetValue(output,
                            TypeUtil.GetType(indexFieldTypeDict[i])?.GetEnumByDescription(record[i]?.ToString() ?? ""), null);
                    }
                    else if ("System.DateTime".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output,
                            ExcelDateUtil.ToDateTime(long.Parse(record[i]?.ToString() ?? "1")), null);
                    }
                    else if ("System.DateTimeOffset".Equals(indexFieldTypeDict[i]))
                    {
                        indexPropertyInfoDict[i].SetValue(output,
                            ExcelDateUtil.ToDateTimeOffset(long.Parse(record[i]?.ToString() ?? "1")), null);
                    }
                }
                if (output != null)
                {
                    outputs.Add((T)output);
                }
            }



            dict = new Dictionary<string, T>();
            foreach (var x in outputs)
            {
                var key = string.Empty;
                foreach (var k in keys)
                {
                    key += StringUtil.SplitChar() + namePropertyInfoDict[k]?.GetValue(x) ?? "";
                }

                if (!string.IsNullOrEmpty(key))
                {
                    if (dict.ContainsKey(key)) throw Oops.Oh($"在{sheetName}页的第{dataStartLine}行, 唯一键冲突");
                    dict.Add(key, x);
                }

                dataStartLine += 1;
            };

            return outputs;
        }



        /// <summary>
        /// 获取已存在的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="records"></param>
        /// <returns></returns>
        public static List<Dictionary<string, object>> ParseUniqueKeyValueDictList<T>(this List<T> records, List<string> keys, int dataStartLine, string sheetName)
        {

            if (keys.Count <= 0)
            {
                throw Oops.Oh(ErrorCode.Key001);
            }

            if (records.Count <= 0)
            {
                return new List<Dictionary<string, object>>();
            }

            Type type = typeof(T);
            List<Dictionary<string, object>> uniqueKeyValueDictList = new();
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => keys.Contains(x.Name)).ToList();
            Dictionary<string, PropertyInfo> namePropDict = new();
            props.ForEach(x => namePropDict.Add(x.Name, x));


            List<string> keyStrs = new();

            records.ForEach(x =>
            {
                Dictionary<string, object> dict = new();
                string keyStr = string.Empty;
                keys.ForEach(y =>
                {
                    var value = namePropDict[y]?.GetValue(x) ?? "";
                    if (string.IsNullOrWhiteSpace(value + "")) throw Oops.Oh($"在{sheetName}页的第{dataStartLine}行, {y}的取值不能为空");
                    keyStr += StringUtil.SplitChar() + value;
                    dict.Add(y, value);
                });
                if (keyStrs.Contains(keyStr)) throw Oops.Oh($"在{sheetName}页的第{dataStartLine}行, 唯一键冲突");
                keyStrs.Add(keyStr);
                uniqueKeyValueDictList.Add(dict);
                dataStartLine++;
            });

            return uniqueKeyValueDictList;
        }

        /// <summary>
        /// 生成动态查询已存在记录的lamda表达式列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keys"></param>
        /// <param name="uniqueKeyValueDictList"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static List<Expression<Func<T, bool>>> GetExpressionListByUniqueDict<T>(List<string> keys,
            List<Dictionary<string, object>> uniqueKeyValueDictList, int size)
        {
            if (keys.Count <= 0) throw Oops.Oh(ErrorCode.Key001);
            var uniqueKeyValueDictListArray = uniqueKeyValueDictList.SplitList(size);
            Type type = typeof(T);
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => keys.Contains(x.Name)).ToList();
            Dictionary<string, PropertyInfo> namePropDict = new();
            props.ForEach(x => namePropDict.Add(x.Name, x));
            List<Expression<Func<T, bool>>> filters = new();
            foreach (var dictList in uniqueKeyValueDictListArray)
            {
                var parameter = Expression.Parameter(type, "m");
                Expression? expression = null;
                foreach (var item in dictList)
                {
                    Expression? expressionItem = null;
                    foreach (var key in keys)
                    {
                        Expression expProperty = Expression.Property(parameter, key);
                        Expression<Func<object?>> valueLamda = () => item[key];
                        Expression expValue = Expression.Convert(valueLamda.Body, namePropDict[key].PropertyType);
                        expressionItem = expressionItem == null ? Expression.Equal(expProperty, expValue) :
                        Expression.AndAlso(expressionItem, Expression.Equal(expProperty, expValue));
                    }
                    expression = (expression == null) ? expressionItem : Expression.OrElse(expression, expressionItem ?? null!);
                }

                Expression<Func<T, bool>> filter = ((Expression<Func<T, bool>>)Expression.Lambda(expression ?? null!, parameter));
                filters.Add(filter);
            }
            return filters;
        }

        /// <summary>
        /// 生成动态查询已存在记录的投影lamda表达式列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="S"></typeparam>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static Expression<Func<TSource, TResult>> GetSelectExpressionListByUniqueDict<TSource, TResult>(List<string> keys)
        {
            if (keys.Count <= 0)
            {
                throw Oops.Oh(ErrorCode.Key001);
            }
            Type type = typeof(TSource);
            var left = Expression.Parameter(type, "x");
            var v0 = Expression.New(typeof(TResult));
            List<MemberBinding> bindingList = new();
            foreach (var item in keys)
            {
                MemberInfo speciesMember = typeof(TResult).GetMember(item)[0];
                MemberExpression mem = Expression.Property(left, item);
                MemberBinding memberBinding = Expression.Bind(speciesMember, mem);
                bindingList.Add(memberBinding);
            }
            Expression body = Expression.MemberInit(v0, bindingList);
            Expression<Func<TSource, TResult>> selector = (Expression<Func<TSource, TResult>>)Expression.Lambda(body, left);
            return selector;
        }

        /// <summary>
        /// 获取字典生成的键字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static string GetKey<T>(T t, string[] keys)
        {
            Type type = typeof(T);
            List<Dictionary<string, object>> uniqueKeyValueDictList = new();
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => keys.Contains(x.Name)).ToList();
            Dictionary<string, PropertyInfo> namePropDict = new();
            props.ForEach(x => namePropDict.Add(x.Name, x));
            string key = "";
            foreach (var item in keys)
            {
                key += StringUtil.SplitChar() + namePropDict[item]?.GetValue(t) ?? "";
            }
            return key;
        }
    }
}